#ifndef UTIL_HPP
#define UTIL_HPP
// DNS
#include <arpa/inet.h>
#include <netdb.h>
// IP
#include <ifaddrs.h>

#include <queue>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <iomanip>

#include <regex>

using namespace std;

string DNS_resolver(string domain) {
    if (domain.empty()) {
        exit(1);
    };
    struct hostent* host=gethostbyname(domain.c_str());
    if (!host) {
        exit(1);
    }
    return inet_ntoa(*(in_addr*)host->h_addr);
}

queue<string> IP_config(){
    queue<string> res;
	struct ifaddrs * ifAddrStruct=NULL;
    void * tmpAddrPtr=NULL;
    getifaddrs(&ifAddrStruct);
    while (ifAddrStruct!=NULL) {
        if (ifAddrStruct->ifa_addr->sa_family==AF_INET) {
            // check it is IP4
            // is a valid IP4 Address
            tmpAddrPtr = &((struct sockaddr_in *)ifAddrStruct->ifa_addr)->sin_addr;
            char addressBuffer[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
            // #ifdef DEBUG
            // printf("%s IPV4 Address %s\n", ifAddrStruct->ifa_name, addressBuffer);
            // #endif
            res.push(addressBuffer);
        } else if (ifAddrStruct->ifa_addr->sa_family==AF_INET6) {
            // check it is IP6
            // is a valid IP6 Address
            tmpAddrPtr=&((struct sockaddr_in *)ifAddrStruct->ifa_addr)->sin_addr;
            char addressBuffer[INET6_ADDRSTRLEN];
            inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
            // #ifdef DEBUG
            // printf("%s IPV6 Address %s\n", ifAddrStruct->ifa_name, addressBuffer);
            // #endif
        } 
        ifAddrStruct = ifAddrStruct->ifa_next;
    }
	return res;
}

void ARGS_resolver(int argc, char** argv, int& PORT, string& config) {
    queue<string> args;
    for(int i=1; i<argc; i++) {
        args.push(argv[i]);
    }
    while(!args.empty()) {
        string temp = args.front();
        if(temp == "--help" || temp == "-h") {
        } else if(temp == "--port" || temp == "-p") {
            args.pop();
            temp = args.front();
            regex r("\\d+");
            bool flag = regex_match(temp, r);
            if(flag) {
                PORT = stoi(temp);
            } else {
                cerr << "error port: " << temp << endl;
                exit(1);
            }
        } else if(temp == "--config" || temp == "-c") {
            args.pop();
            temp = args.front();
            fstream fp(temp);
            if(fp) {
                string temp;
                while (getline(fp, temp)) {
                    config += temp + '\n';
                }
            } else {
                cerr << "error port: " << temp << endl;
                exit(1);
            }
        } else {
            regex r("\\d+");
            bool flag = regex_match(temp, r);
            if(flag) {
                PORT = stoi(temp);
            } else {
                cerr << "error port: " << temp << endl;
                exit(1);
            }
        }
        args.pop();
    }
    if(config.empty()) {
        fstream fp("./config/little_nginx.conf");
        if(fp) {
            string temp;
            while (getline(fp, temp)) {
                config += temp + '\n';
            }
        } else {
            cerr << "error port: " << endl;
            exit(1);
        }
    }
}

string concat_host_port(string A, int B) {
    int cnt = 0;
    int tempB = B;
    while(tempB) {
        cnt++;
        tempB /= 10;
    }
    char* temp = new char[cnt + A.size() + 1];
    sprintf(temp, "%s:%d", A.c_str(), B);
    A = temp;
    delete[] temp;
    return A;
}

string get_time(string fmt = "") {
    char week_days[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
    char months[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
    time_t rawtime;
    struct tm * timeinfo;
    time ( &rawtime );
    timeinfo = localtime ( &rawtime );
    string res = "";
    stringstream strcout;
    if(!fmt.empty()) {
        for(int i=0;i<fmt.size();i++){
            if(fmt[i] == '%') {
                char op = fmt[++i];
                switch (op) {
                    case 'Y':
                        strcout << timeinfo->tm_year+1900;
                        break;
                    case 'M':
                        strcout << timeinfo->tm_mon+1;
                        break;
                    case 'E':
                        strcout << months[timeinfo->tm_mon];
                        break;
                    case 'W':
                        strcout << week_days[timeinfo->tm_wday];
                        break;
                    case 'Z':
                        strcout << timeinfo->tm_zone;
                        break;
                    case 'D':
                        strcout << timeinfo->tm_mday;
                        break;
                    case 'h':
                        strcout << setw(2) << setfill('0') << timeinfo->tm_hour;
                        break;
                    case 'm':
                        strcout << setw(2) << setfill('0') << timeinfo->tm_min;
                        break;
                    case 's':
                        strcout << setw(2) << setfill('0') << timeinfo->tm_sec;
                        break;
                    default:
                        break;
                }
            } else {
                strcout << fmt[i];
            }
        }
    } else {
        strcout << asctime(timeinfo);
    }
    getline(strcout, res);
    return res;
}

int get_time_stamp() {
    time_t rawtime;
    time ( &rawtime );
    return rawtime;
}

#endif